﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using YasharEl.Infrastructure.ApplicationModel.Modularity.Bootstrap;
using YasharEl.Infrastructure.ApplicationModel.Bootstrap.Configuration;
using YasharEl.Infrastructure.ExceptionHandling.Configuration;
using System.Configuration;
using YasharEl.Infrastructure.ExceptionHandling;

namespace YasharEl.Infrastructure.ApplicationModel.Bootstrap.Extensions
{
    public class ExceptionHandlingBootstrapperExtension : ModuleComponentBootstrappingExtension, IConsumeConfigurationSection
    {
        #region Properties

        public ExceptionHandlingConfig ExceptionHandlingConfigSection { get; private set; }

        #endregion

        #region IConsumeConfigurationSection Members

        public void Apply(ConfigurationSection section)
        {
            ExceptionHandlingConfigSection = section as ExceptionHandlingConfig;
        }

        #endregion

        #region BaseComplexBootstrappingExtension Describe

        public override string Describe()
        {
            return "Extension which configure and start exception handling infrastructure component.";
        }

        #endregion

        #region ModuleComponentBootstrappingExtension Overrides

        public override string SectionName
        {
            get { return ExceptionHandlingConfig.ExceptionHandlingConfigDefaultSectionName; }
        }

        #endregion

        #region BaseComplexBootstrappingExtension Configure

        public override void Configure()
        {
            base.Configure();

            IExceptionManager exceptionManager;

            if (ExceptionHandlingConfigSection != null)
            {
                // Register default singleton message translator.
                // May be configured to use resources management for resolving string by resource key.
                Container.DependencyRegistrar.Register(
                    typeof(IMessageTranslator),
                    ExceptionHandlingConfigSection.MessageTranslatorType,
                    true
                );

                Dictionary<string, IExceptionPolicy> exceptionPolicies = new Dictionary<string, IExceptionPolicy>();
                foreach (ExceptionPolicyConfig exceptionPolicyConfig in ExceptionHandlingConfigSection.ExceptionPolicies)
                {
                    Dictionary<Type, ExceptionPolicyEntry> policyEntries = new Dictionary<Type, ExceptionPolicyEntry>();

                    Logger.Info("Configuring exception handling policy with name : {0}", exceptionPolicyConfig.PolicyName);

                    #region ExceptionTypes

                    foreach (ExceptionTypeConfig exceptionTypeConfig in exceptionPolicyConfig.ExceptionTypes)
                    {
                        List<IExceptionHandler> exceptionHandlers = new List<IExceptionHandler>();

                        Logger.Info("Exception handling policy with name : {0} contains and configure exception type {1}",
                            exceptionPolicyConfig.PolicyName, exceptionTypeConfig.ExceptionType);

                        #region ForEach Exception Handler

                        foreach (ExceptionHandlerConfig exceptionHandlerConfig in exceptionTypeConfig.ExceptionHandlers)
                        {
                            IExceptionHandler exceptionHandler = exceptionHandlerConfig.CreateExceptionHandler(Container.DependencyResolver);

                            exceptionHandlers.Add(exceptionHandler);

                            Logger.Info(
                                "Exception handling policy with name : {0}, exception type {1} successfully created handler {2}",
                                    exceptionPolicyConfig.PolicyName,
                                    exceptionTypeConfig.ExceptionType,
                                    exceptionHandler.GetType().FullNameToString()
                            );
                        }

                        #endregion

                        ExceptionPolicyEntry exceptionPolicyEntry = new ExceptionPolicyEntry(
                            exceptionTypeConfig.ExceptionType,
                            exceptionTypeConfig.PostHandlingAction,
                            exceptionHandlers);

                        if (policyEntries.ContainsKey(exceptionPolicyEntry.ExceptionType))
                        {
                            throw new InvalidSystemConfigurationException(
                                string.Format(@"Exception handling policy {0} not valid. Exception type {1} occurs more that once.",
                                    exceptionPolicyConfig.PolicyName, exceptionPolicyEntry.ExceptionType));
                        }
                        policyEntries.Add(exceptionPolicyEntry.ExceptionType, exceptionPolicyEntry);
                    }

                    #endregion

                    IExceptionPolicy exceptionPolicy = new ExceptionPolicy(
                        exceptionPolicyConfig.PolicyName, policyEntries);

                    exceptionPolicies.Add(exceptionPolicy.PolicyName, exceptionPolicy);
                }

                exceptionManager = new DefaultExceptionManager(exceptionPolicies);
            }
            else
            {
                Logger.Warn("There is no exception handling configuration found. Disabled exception manager instantiated by default.");

                exceptionManager = new DisabledExceptionHandlingManager();

                Logger.Warn("There is no exception handling configuration found. Disabled message translation instantiated by default. Resource key returned as resolved string.");

                // Register default singleton message translator with disabled behaviour.
                IMessageTranslator messageTranslator = new DisabledMessageTranslator();

                Container.DependencyRegistrar.Register<IMessageTranslator>(
                    messageTranslator,
                    true
                );
            }

            // Register exception manager visible from container.
            Container.DependencyRegistrar.Register<IExceptionManager>(exceptionManager);

            Logger.Info("Exception handling manager({0}) configured and registered to container...", exceptionManager.GetType());
        }

        #endregion
    }
}
